package org.dei.perla.parser;

import java.util.ArrayList;
import java.util.Iterator;

import org.dei.perla.parser.expressions.Constant;
import org.dei.perla.parser.expressions.ConstantID;
import org.dei.perla.parser.statements.Query;
import org.dei.perla.parser.util.*;
import org.dei.perla.utils.pipe.*;
import org.dei.perla.utils.waiter.Waitable;
import org.dei.perla.utils.waiter.Waiter;

import org.dei.perla.sys.device.fpc.FPC;
import org.dei.perla.sys.llqruntime.QueryAnalyzer;

public class QueryExecutionAgent extends Thread{

	private static QueryExecutionAgent agent = null;
	private static ArrayList<FPC> fpcList = null;
	private static Waiter<Pipe<Query>> waiter = null;
	
	private QueryExecutionAgent(){
		
	}
	
	public static QueryExecutionAgent getDefault() {
		if(agent == null)
			agent = new QueryExecutionAgent();
		return agent;
	}

	public static void setFPCList(ArrayList<FPC> fpcList){
		QueryExecutionAgent.fpcList = fpcList; 
	}
	
	public void setQueryToExecutePipe(Pipe<Query> pipe){
		waiter = new Waiter<Pipe<Query>>();
		waiter.start();
		waiter.addWaitable(pipe.getWaitable());
	}
	
	public void execute(Query parsedQuery) throws QueryExecutionAgentFailure {
		// TODO Rimuovere in una successiva release
		ArrayList<PairValue<String, ? extends Constant>> pAttributeToSetList = new ArrayList<PairValue<String, ? extends Constant>>();
		QueryAnalyzer queryAnalizer = new QueryAnalyzer(parsedQuery);
		for (String tAttributeToSet : queryAnalizer.getAttributeToSet().keySet()){
			pAttributeToSetList.add(new PairValue<String, Constant>(tAttributeToSet, queryAnalizer.getAttributeToSet().get(tAttributeToSet)));
		}
		if(fpcList != null){
			for (ConstantID id : queryAnalizer.getDeviceToSetID()) {
				fpcList.get(id.getValueInt()).setUpdateAttributes(pAttributeToSetList);
			}
		}
		else{
			throw new QueryExecutionAgentFailure("FPC list is null");
		}
	}
	
	public void run(){
		Waitable<Pipe<Query>> waitable;
		Query toExecute;

		while(true) {
			waitable = waiter.waitNext();
			toExecute = waitable.getParentComponent().dequeue();
			if (toExecute != null) {
				try {
					execute(toExecute);
				} catch (QueryExecutionAgentFailure e) {
					// TODO Sistemare con un meccanismo simile a errlog
					e.printStackTrace();
				}
			} else {
				// Killpacket ricevuto, chiudo la connessione con il DB,
				// termino le pipe, termino il processo
				waiter.removeWaitable(waitable);
				waitable.getParentComponent().stop();
				waiter.stop();
				//TODO rivedere questo punto
				break;
			}
		}
	}
	
	
}
